#include "os_config.h"
#include <stdint.h>


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// #include <dirent.h>
// #include <fcntl.h>
#include <getopt.h>
// #include <signal.h>
// #include <unistd.h>

// #include "ak_common.h"
// #include "ak_log.h"
// #include "ak_thread.h"
// #include "ak_mem.h"


// #include "../../../os/kernel_rtt/bsp/anyka/misc/delay.h"

#ifdef AK_RTOS
#include <kernel.h>
#define THREAD_PRIO 90
#define __STATIC__  static
#else
#define THREAD_PRIO -1
#define __STATIC__
#endif



#define OSAPI_T_LOG  rt_kprintf
#define OSAPI_T_LOG3 rt_kprintf


#define AK_FALSE false
#define AK_TRUE true
 
#define LEN_HINT                    512
#define LEN_PATH                    1024
#define LEN_DATA                    256
#define LEN_OPTION_SHORT            512


/* this is the message to print */
static const char ac_option_hint[  ][ LEN_HINT ] = {                                  //操作提示数组
    "打印帮助信息" ,
    "" ,
};

/* opt for print the message */
static const struct option option_long[ ] = {
    { "help"              , no_argument       , NULL , 'h' } ,                  //"打印帮助信息" ,
	{ 0                   , 0                 , 0    , 0   } ,                  //"" ,
};

static char *pc_prog_name = NULL;   

/*
* get_option_short: fill the stort option string.
* return: option short string addr.
*/
static char *get_option_short( struct option *p_option, int i_num_option, char *pc_option_short, int i_len_option )
{
    int i;
    int i_offset = 0;
    char c_option;

    /* get the option */
    for( i = 0 ; i < i_num_option ; i ++ )
    {
        /* get the opt */
        if( ( c_option = p_option[ i ].val ) == 0 )
        {
            continue;
        }

        switch( p_option[ i ].has_arg )
        {
        case no_argument:
            /* if no argument, set the offset for default */
            i_offset += snprintf( pc_option_short + i_offset , i_len_option - i_offset , "%c" , c_option );
            break;
        case required_argument:
            /* required argument offset calculate */
            i_offset += snprintf( pc_option_short + i_offset , i_len_option - i_offset , "%c:" , c_option );
            break;
        case optional_argument:
            /* calculate the option offset */
            i_offset += snprintf( pc_option_short + i_offset , i_len_option - i_offset , "%c::" , c_option );
            break;
        }
    }
    return pc_option_short;
}

static void usage(const char * name)
{
    /* a example for this sample in usage */

    printf(" \n" );

}

/* if opt is not supported, print the help message */
static int help_hint(void)
{
    int i;

    printf( "%s\n" , pc_prog_name);
    printf( "Usage: \r\n\t%s  [options] <value>\n\n", pc_prog_name);

    /* parse the all supported option */
    printf( "options: \r\n");
    for(i = 0; i < sizeof(option_long) / sizeof(struct option); i ++)
    {
        /* get the opt */
        if( option_long[ i ].val != 0 )
            printf("\t-%c, --%-16s\t%s\n" , option_long[ i ].val , option_long[ i ].name , ac_option_hint[ i ]);
    }

    /* an example */
    usage(pc_prog_name);
    printf( "\n\n");
    return 0;
}

/* parse the option from cmd line */
static int parse_option( int argc, char **argv )
{
    int i_option;

    char ac_option_short[ LEN_OPTION_SHORT ];                           /* opt string */
    int i_array_num = sizeof( option_long ) / sizeof( struct option ) ; /* get the option num*/
    char c_flag = AK_TRUE;
    pc_prog_name = argv[ 0 ];   /* get the option */
    optind = 0;

    get_option_short( option_long, i_array_num , ac_option_short , LEN_OPTION_SHORT );  /* parse the cmd line input */
    while((i_option = getopt_long(argc , argv , ac_option_short , option_long , NULL)) > 0)
    {
        switch(i_option)
        {
        case 'h' :                                                          //help
            help_hint();
            c_flag = AK_FALSE;
            goto parse_option_end;
        case 'w' :                                                          //
            break;

        default :
            help_hint();                                                    //if out of range, print message
            c_flag = AK_FALSE;
            goto parse_option_end;
        }
    }

parse_option_end:
    return c_flag;
}

/*
static void WAIT_KEY()
{

}
*/

static void delay_us(uint16_t us)
{
  #if 0
  do
  {
  	__asm volatile("nop");

  } while (--us);
  #else
	us_delay(us);
  #endif
}

static void delay_sec(uint16_t nsec)
{
	sleep_cust(nsec);
}

void task1_handler(void * p)
{
	uint32_t tick,tick2;
	int ret;
	semaphore_handle_t sem_v = (semaphore_handle_t)p;

	OSAPI_T_LOG("%s %u\n",__FUNCTION__,kal_get_systicks());

	while(1)
	{
		tick = kal_get_systicks();
		os_semaphore_take(sem_v);
		tick2 = kal_get_systicks();
		tick = tick2 - tick;

		OSAPI_T_LOG("os_semaphore_take tick:%u  %u\n",tick2,tick);

		tick = kal_get_systicks();
		ret = os_semaphore_take_waitTick(sem_v,50);
		tick2 = kal_get_systicks();
		tick = tick2 - tick;

		OSAPI_T_LOG("os_semaphore_take_waitTick %d tick:%u  %u\n",ret,tick2,tick);

		delay_ms(100);
	}
}

void task2_handler(void * p)
{
	semaphore_handle_t sem_v = (semaphore_handle_t)p;

	OSAPI_T_LOG("%s %u\n",__FUNCTION__,kal_get_systicks());

	while(1)
	{
		delay_ms(1200);
		
		os_semaphore_release(sem_v);
		OSAPI_T_LOG("os_semaphore_release %u\n",kal_get_systicks());
	}
	
}

void task3_handler(void * p)
{

	mutex_handle_t	mutex_v = (mutex_handle_t)p;

	OSAPI_T_LOG("%s %u\n",__FUNCTION__,kal_get_systicks());

	while(1)
	{
		os_mutex_take(mutex_v);
		OSAPI_T_LOG("%s os_mutex_take %u\n",__FUNCTION__,kal_get_systicks());

		delay_ms(100);
		
		os_mutex_release(mutex_v);

		delay_ms(300);
	}

	
}

void task4_handler(void * p)
{

	mutex_handle_t	mutex_v = (mutex_handle_t)p;

	OSAPI_T_LOG("%s %u\n",__FUNCTION__,kal_get_systicks());

	while(1)
	{
		os_mutex_take(mutex_v);
		OSAPI_T_LOG("%s os_mutex_take %u\n",__FUNCTION__,kal_get_systicks());

		delay_ms(200);
		
		os_mutex_release(mutex_v);

		delay_ms(200);
	}
	
}

void task5_handler(void * p)
{


	OSAPI_T_LOG("%s %u\n",__FUNCTION__,kal_get_systicks());

	while(1)
	{
		delay_ms(1000);
	}
	
}


void msg_queue_task1_handler(void * p)
{
	int msg;
	int ret;
	msg_queue_id msg_qu = (msg_queue_id)p;

	OSAPI_T_LOG("%s %u\n",__FUNCTION__,kal_get_systicks());

	msg = 0;

	while(1)
	{
		ret = msg_enqueue(msg_qu,&msg);
		//if(ret != 0)
		//{
		//	OSAPI_T_LOG3("msg_enqueue err\n");
		//}
		
		OSAPI_T_LOG("msg_enqueue %s %u\n",ret != 0 ? "err": "OK", kal_get_systicks());
		
		delay_ms(500);

		++msg;
	}
	
}


void msg_queue_task2_handler(void * p)
{
	int msg;
	int ret;
	msg_queue_id msg_qu = (msg_queue_id)p;


	OSAPI_T_LOG("%s %u\n",__FUNCTION__,kal_get_systicks());

	while(1)
	{
		ret = msg_dequeue(msg_qu,&msg);
		if(ret != 0)
			OSAPI_T_LOG3("msg_dequeue err %u\n",kal_get_systicks());
		else
			OSAPI_T_LOG3("msg_dequeue %u msg:%d\n",kal_get_systicks(),msg);
		
		ret = msg_dequeue_waitTick(msg_qu,&msg,450);
		if(ret != 0)
			OSAPI_T_LOG3("msg_dequeue_waitTick err %u\n",kal_get_systicks());
		else
			OSAPI_T_LOG3("msg_dequeue_waitTick %u msg:%d\n",kal_get_systicks(),msg);
		
	}
	
}


#define CUSTOM_EVENT_1    0x0001

void events_task1_handler(void * p)
{
	int ret;
	my_events_id event_v = (my_events_id)p;

	OSAPI_T_LOG("%s %u\n",__FUNCTION__,kal_get_systicks());


	while(1)
	{
		ret = my_events_set(event_v,CUSTOM_EVENT_1);
		//if(ret != 0)
		//{
		//	OSAPI_T_LOG3("msg_enqueue err\n");
		//}
		
		OSAPI_T_LOG("my_events_set %s %u\n",ret != 0 ? "err": "OK", kal_get_systicks());
		
		delay_ms(500);

	}
	
}


void events_task2_handler(void * p)
{
	int ret;
	my_events_id event_v = (my_events_id)p;


	OSAPI_T_LOG("%s %u\n",__FUNCTION__,kal_get_systicks());

	while(1)
	{
		ret = my_events_wait(event_v,CUSTOM_EVENT_1);
		OSAPI_T_LOG("my_events_wait %s %u\n",ret != 0 ? "err": "OK", kal_get_systicks());
		
		ret = my_events_wait_tick(event_v,CUSTOM_EVENT_1,450);
		OSAPI_T_LOG("my_events_wait_tick %s %u\n",ret != 0 ? "err": "OK", kal_get_systicks());
		
	}
	
}

void timer1_handler(void *p)
{
	OSAPI_T_LOG("-------------------%s %u\n",__FUNCTION__,kal_get_systicks());
}

void timer2_handler(void *p)
{
	OSAPI_T_LOG("--------%s %u\n",__FUNCTION__,kal_get_systicks());
}

void __os_api_test(void)
{
	semaphore_handle_t sem_v = NULL;
	mutex_handle_t		mutex_v = NULL;
	msg_queue_id		msg_qu = NULL;
	my_events_id		event_v = NULL;

	TimerId_id       timer1_v = NULL;
	TimerId_id       timer2_v = NULL;
	
	task_handle_t task1 = INVALID_TASK;
	task_handle_t task2 = INVALID_TASK;
	task_handle_t task3 = INVALID_TASK;
	task_handle_t task4 = INVALID_TASK;
	task_handle_t task5 = INVALID_TASK;
	
	task_handle_t msg_qu_task1 = INVALID_TASK;
	task_handle_t msg_qu_task2 = INVALID_TASK;

	task_handle_t event_task1 = INVALID_TASK;
	task_handle_t event_task2 = INVALID_TASK;

	uint32_t  tick;


	tick = kal_get_systicks();
	delay_us(10000);
	OSAPI_T_LOG("--> delay_us(10000) %u\n",kal_get_systicks()-tick);

	tick = kal_get_systicks();
	delay_ms(12);
	OSAPI_T_LOG("--> delay_ms(12) %u\n",kal_get_systicks()-tick);

	tick = kal_get_systicks();
	delay_ms(123);
	OSAPI_T_LOG("--> delay_ms(123) %u\n",kal_get_systicks()-tick);

	tick = kal_get_systicks();
	delay_ms(1234);
	OSAPI_T_LOG("--> delay_ms(1234) %u\n",kal_get_systicks()-tick);

	tick = kal_get_systicks();
	delay_sec(3);
	OSAPI_T_LOG("--> delay_sec(3) %u\n",kal_get_systicks()-tick);

	

	
	sem_v = os_semaphore_create("abc");
	if(sem_v == NULL)
	{
		OSAPI_T_LOG3("os_semaphore_create err\n");
		return ;
	}

	mutex_v = os_mutex_create("mutex");
	if(mutex_v == NULL)
	{
		OSAPI_T_LOG3("os_mutex_create err\n");
		goto  TASK_FAIL;
	}

	msg_qu = msg_queue_create("msg",sizeof(int),3);
	if(msg_qu == NULL)
	{
		OSAPI_T_LOG3("msg_queue_create  err\n");
		goto  TASK_FAIL;
	}

	event_v = my_events_create("event");
	if(event_v == NULL)
	{
		OSAPI_T_LOG3("my_events_create  err\n");
		goto  TASK_FAIL;
	}

	timer1_v = mytimer_create("timer1_v");
	if(timer1_v == NULL)
	{
		OSAPI_T_LOG3("mytimer_create 1 err\n");
		goto  TASK_FAIL;
	}

	timer2_v = mytimer_create("timer2_v");
	if(timer2_v == NULL)
	{
		OSAPI_T_LOG3("mytimer_create 2 err\n");
		goto  TASK_FAIL;
	}


	task1 = os_task_create("task1",task1_handler,sem_v,TASK_STACK_MIN,TASK_PRIO_M,10);
	if(task1 == INVALID_TASK)
	{
		OSAPI_T_LOG3("os_task_create 1 err\n");
		goto  TASK_FAIL;
	}


	task2 = os_task_create("task2",task2_handler,sem_v,TASK_STACK_MIN,TASK_PRIO_M,10);
	if(task2 == INVALID_TASK)
	{
		OSAPI_T_LOG3("os_task_create 2 err\n");
		goto  TASK_FAIL;
	}


	task3 = os_task_create("task3",task3_handler,mutex_v,TASK_STACK_MIN,TASK_PRIO_M,10);
	if(task3 == INVALID_TASK)
	{
		OSAPI_T_LOG3("os_task_create 3 err\n");
		goto  TASK_FAIL;
	}


	task4 = os_task_create("task4",task4_handler,mutex_v,TASK_STACK_MIN,TASK_PRIO_M,10);
	if(task4 == INVALID_TASK)
	{
		OSAPI_T_LOG3("os_task_create 4 err\n");
		goto  TASK_FAIL;
	}

	
	task5 = os_task_create("task5",task5_handler,NULL,TASK_STACK_MIN,TASK_PRIO_M,10);
	if(task5 == INVALID_TASK)
	{
		OSAPI_T_LOG3("os_task_create 5 err\n");
		goto  TASK_FAIL;
	}

	msg_qu_task1 = os_task_create("msg_qu_task1",msg_queue_task1_handler,msg_qu,TASK_STACK_MIN,TASK_PRIO_M,10);
	if(msg_qu_task1 == INVALID_TASK)
	{
		OSAPI_T_LOG3("os_task_create msg_qu_task1 err\n");
		goto  TASK_FAIL;
	}

	msg_qu_task2 = os_task_create("msg_qu_task2",msg_queue_task2_handler,msg_qu,TASK_STACK_MIN,TASK_PRIO_M,10);
	if(msg_qu_task2 == INVALID_TASK)
	{
		OSAPI_T_LOG3("os_task_create msg_qu_task2 err\n");
		goto  TASK_FAIL;
	}

	event_task1 = os_task_create("event_task1",events_task1_handler,event_v,TASK_STACK_MIN,TASK_PRIO_M,10);
	if(event_task1 == INVALID_TASK)
	{
		OSAPI_T_LOG3("os_task_create event_task1 err\n");
		goto  TASK_FAIL;
	}

	event_task2 = os_task_create("event_task2",events_task2_handler,event_v,TASK_STACK_MIN,TASK_PRIO_M,10);
	if(event_task2 == INVALID_TASK)
	{
		OSAPI_T_LOG3("os_task_create event_task2 err\n");
		goto  TASK_FAIL;
	}

	mytimer_open_v2(timer1_v,1000,timer1_handler,NULL,true);

	mytimer_open_v2(timer2_v,1300,timer2_handler,NULL,false);

	WAIT_KEY();

TASK_FAIL:

	OSAPI_T_LOG("--------over-------\n");

	if(timer1_v)
	{
		mytimer_close(timer1_v);
		mytimer_free(&timer1_v);
	}

	if(timer2_v)
	{
		mytimer_close(timer2_v);
		mytimer_free(&timer2_v);
	}


	//WAIT_KEY();


	if(task1 != INVALID_TASK)
		os_task_delete(&task1);
	
	if(task2 != INVALID_TASK)
		os_task_delete(&task2);
	
	if(task3 != INVALID_TASK)
		os_task_delete(&task3);
	
	if(task4 != INVALID_TASK)
		os_task_delete(&task4);

	if(task5 != INVALID_TASK)
		os_task_delete(&task5);

	if(msg_qu_task1 != INVALID_TASK)
		os_task_delete(&msg_qu_task1);

	if(msg_qu_task2 != INVALID_TASK)
		os_task_delete(&msg_qu_task2);

	if(event_task1 != INVALID_TASK)
		os_task_delete(&event_task1);

	if(event_task2 != INVALID_TASK)
		os_task_delete(&event_task2);


	//WAIT_KEY();
	

	if(sem_v)
		os_semaphore_delete(sem_v);

	if(mutex_v)
		os_mutex_delete(mutex_v);

	if(msg_qu)
		msg_queue_free(msg_qu);

	if(event_v)
		my_events_free(&event_v);


	WAIT_KEY();

}


void timer1_tst_handler(void *p)
{
	semaphore_handle_t sem_v = (semaphore_handle_t)p;


	OSAPI_T_LOG("timer1 %x\n",(unsigned int)pthread_self());

	os_semaphore_release(sem_v);
}

void timer2_tst_handler(void *p)
{
	semaphore_handle_t sem_v = (semaphore_handle_t)p;
	
	OSAPI_T_LOG("timer2 %x\n",(unsigned int)pthread_self());

	os_semaphore_release(sem_v);
}

void timer3_tst_handler(void *p)
{
	OSAPI_T_LOG("timer3 %x\n",(unsigned int)pthread_self());
}

void timer_common_handler(void* p)
{
	OSAPI_T_LOG("-----%s %u %x\n",p != NULL ? (const char*)p:"T",kal_get_systicks(),(unsigned int)pthread_self());
}

void os_timer_api_test(void)
{
	int count;
	int ret;
	
	semaphore_handle_t sem_v1 = NULL;
	semaphore_handle_t sem_v2 = NULL;
	
	TimerId_id       timer1_v = NULL;
	TimerId_id       timer2_v = NULL;
	TimerId_id       timer3_v = NULL;
	
	kal_uint32 tick;

	sem_v1 = os_semaphore_create("abc");
	if(sem_v1 == NULL)
	{
		OSAPI_T_LOG3("os_semaphore_create err\n");
		return ;
	}

	sem_v2 = os_semaphore_create("abcd");
	if(sem_v2 == NULL)
	{
		OSAPI_T_LOG3("os_semaphore_create err\n");
		return ;
	}

	timer1_v = mytimer_create("timer1_v");
	if(timer1_v == NULL)
	{
		OSAPI_T_LOG3("mytimer_create 1 err\n");
		goto  FAIL_FLAG;
	}
	
	timer2_v = mytimer_create("timer2_v");
	if(timer2_v == NULL)
	{
		OSAPI_T_LOG3("mytimer_create 2 err\n");
		goto  FAIL_FLAG;
	}
	
	timer3_v = mytimer_create("timer3_v");
	if(timer3_v == NULL)
	{
		OSAPI_T_LOG3("mytimer_create 3 err\n");
		goto  FAIL_FLAG;
	}

//-------------------------------------------
	system("date");
	delay_sec(1);

	system("ntpd");
	delay_sec(3);

	system("date");
	delay_sec(1);

	OSAPI_T_LOG("<------systick: %u\n",kal_get_systicks());

	mytimer_open_v2(timer1_v,2000,timer_common_handler,"timer1",false);
	mytimer_open_v2(timer2_v,4000,timer_common_handler,"timer2",false);
	mytimer_open_v2(timer3_v,6000,timer_common_handler,"timer3",false);
	delay_sec(1);

	system("date -s '2021-08-02 17:30:0'");
	system("date");
	
	
	delay_sec(10);

	OSAPI_T_LOG(">------systick: %u\n",kal_get_systicks());
	mytimer_close(timer1_v);
	mytimer_close(timer2_v);
	mytimer_close(timer3_v);


	system("ntpd");

	delay_sec(3);

	WAIT_KEY();


//-------------------------------------------

	count = 0;

	while(1)
	{

		ret = mytimer_open_v2(timer1_v,100,timer1_tst_handler,sem_v1,false);
		ret = mytimer_open_v2(timer2_v,100,timer2_tst_handler,sem_v2,false);
		ret = mytimer_open_v2(timer3_v,90,timer3_tst_handler,NULL,false);

		tick = kal_get_systicks();


		ret = os_semaphore_take_waitTick(sem_v1,200);
		if(ret != 0)
		{
			OSAPI_T_LOG("-------- os_semaphore_take_waitTick1 %u?\n",kal_get_systicks()-tick);
		}

		ret = os_semaphore_take_waitTick(sem_v2,200);
		if(ret != 0)
		{
			OSAPI_T_LOG("-------- os_semaphore_take_waitTick2 %u?\n",kal_get_systicks()-tick);
			fflush(stdout);
			continue;
		}


		++count;

		OSAPI_T_LOG("os timer %d %u\n",count,kal_get_systicks()-tick);
	
	}


FAIL_FLAG:


	if(timer1_v)
	{
		mytimer_close(timer1_v);
		mytimer_free(&timer1_v);
	}

	
	if(timer2_v)
	{
		mytimer_close(timer2_v);
		mytimer_free(&timer2_v);
	}

	if(timer3_v)
	{
		mytimer_close(timer3_v);
		mytimer_free(&timer3_v);
	}

	if(sem_v1)
		os_semaphore_delete(sem_v1);

	if(sem_v2)
		os_semaphore_delete(sem_v2);
}



__STATIC__ int main(int argc, char **argv)
{
	
	/* start the application */
	// sdk_run_config config = {0};
	// config.mem_trace_flag = SDK_RUN_NORMAL;
	// ak_sdk_init( &config );
	
	
	if( parse_option( argc, argv ) == 0 )
	{
		return 0;
	}

	// ak_print_normal(MODULE_ID_APP, "*****************************************\n");
	// ak_print_normal(MODULE_ID_APP, "** os_api_test_sample **\n"  );
	// ak_print_normal(MODULE_ID_APP, "*****************************************\n");


	__os_api_test();

	return 0;
}


#if defined(__RTT_OS__)
SHELL_CMD_EXPORT_ALIAS(main, os_sample, os test Sample);
#endif


